package org.melanesia.ref;

import java.lang.reflect.Method;

/**
 * Generic, reflection-based implementation of the {@link ReleasableResource}.
 *
 * @author marcin.kielar
 * @param <T>
 *            resource type
 */
public class ReflectionFinalizer<T> extends ReleasableResource<T> {

    /** Release method. */
    private final Method releaseMethod;

    /** IsRelease method. */
    private final Method isReleasedMethod;

    /**
     * Constructs new {@code ReflectionFinalizer}. The {@link #releaseMethod}
     * and {@link #isReleasedMethod} are looked up in an eager way during the
     * construct of the object.
     *
     * @param resource
     *            releasable resource
     * @param releaseMethod
     *            name of the method for releasing the resource
     * @param isReleasedMethod
     *            name of the method identifying if the resource has been closed
     *            already
     */
    public ReflectionFinalizer(final T resource, final String releaseMethod, final String isReleasedMethod) {
        super(resource);

        try {
            this.releaseMethod = resource.getClass().getDeclaredMethod(releaseMethod);
            this.isReleasedMethod = resource.getClass().getDeclaredMethod(isReleasedMethod);
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    @Override
    public final void release() {
        try {
            T resource = get();
            if (resource != null && !isReleased()) {
                releaseMethod.invoke(resource);
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    @Override
    public final boolean isReleased() {
        try {
            T resource = get();
            if (resource != null) {
                return (Boolean) isReleasedMethod.invoke(resource);
            } else {
                return true;
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}
